<?php

/**
 * Session支持
 *
 * Sparrow提供的session支持，支持三种存储Session的方式
 * <ol>
 *  <li>文件级Session --- 可以自定义Session存储路径</li>
 *  <li>数据库Session --- 将Session存入数据库，通常在单数据库应用中用于多台Web共享Session</li>
 *  <li>基于Memcache的Session --- 将Session存入Memcached，在大规模应用（特别是具有数据库集群）中用于Session共享</li>
 * </ol>
 *
 */

/**
 * 获取session实例
 *
 * 通过此方法可获取session实例，获取后即可开启或操作session
 * 相关配置
 * <code>
 * // 使用文件存储session
 * session.type -filebased
 * session.path
 *
 * // 使用数据库存储session
 * session.type -database
 * session.db
 * session.table
 *
 * // 使用memcache存储session
 * session.type -memcached
 * session.servers
 * </code>
 * <br />用例<br />
 * <code>
 * $session = session();
 * $session->start();
 * // 像往常一样操作session
 * $_SESSION['test'] = 'test';
 * </code>
 */

define ( 'SESSION_MAX_LIFE_TIME', 1800 );
define("SESSION_TYPE",'database'); // database memcached filebased
// database
// define ( 'SESSION_DB', 1800 );
// define ( 'SESSION_TABLE', 'session' );
// end database
// memcached
/**
define ( 'SESSION_SERVER', '' );
define ( 'SESSION_PATH', '' );
**/
// end memcached

// session()->start();
// $_SESSION['test'] = '数据库里要多一值啊';


function session() {
	static $session = null;
	if ($session === null) {
		$type = SESSION_TYPE; // config here!!!
		$max_life_time = SESSION_MAX_LIFE_TIME ? SESSION_MAX_LIFE_TIME : 1800;
		$session_class = ucfirst ( $type ) . 'Session';
		$session = BaseSession::create ( $session_class );
		$session->setMaxLifeTime ( $max_life_time );
		
		if (isset($_POST["PHPSESSID"])) {
			session_id($_POST["PHPSESSID"]);
		} else if (isset($_GET["PHPSESSID"])) {
			session_id($_GET["PHPSESSID"]);
		}
		
		
		
		switch ($type) {
			case 'database' :
				//$get_db_connection = SESSION_DB;
				$table_name = 'session';
				//$db = $get_db_connection ();
// 				$db = spClass('spModel')->_db;
				$db = new DatabaseConnect( array('host'=>'localhost','port'=>'3306','login'=>'root','password'=>'123456','database'=>'yangtzeu2'));

				$session->setDatabaseConnection ( $db );
				$session->setTable ( $table_name );
				break;
			case 'memcached' :
				$session->addServers ( SESSION_SERVER );
				break;
			// 默认filebased
			default :
				$session->setSavePath ( SESSION_PATH );
				break;
		}
	}
	
	return $session;
}

/**
 * 基础session支持类
 *
 * session基础类，有几点
 * <ol>
 * <li>除了过期时间外，完全使用PHP内置Session的各项功能(session_id生成，指定保存路径等.)</li>
 * <li>ID使用PHP内置ID，要获取当前session_id，通过session_id()函数</li>
 * </ol>
 */
abstract class BaseSession {
	/**
	 * 当前Session实例
	 */
	public static $session = null;
	
	/**
	 * Session过期时间
	 */
	protected $_max_life_time = 1800;
	
	/**
	 * 获取Session实例
	 *
	 * 由于在一次请求中不能存在两个Session，因此采用单例模式，通过该方法获取Session实例
	 *
	 * @author Garbin
	 * @access public
	 * @param string $session_class
	 *        	Session类名
	 * @return Object 返回Session实例
	 *        
	 */
	public static function create($session_class) {
		if (self::$session === null) {
			self::$session = new $session_class ();
		}
		
		return self::$session;
	}
	
	/**
	 * 启动session
	 *
	 * 调用该方法开启Session，开启成功后，您就可以直接像往常一样操作$_SESSION变量了。
	 *
	 * @author Garbin
	 * @access public
	 * @return void
	 *
	 */
    public function start()
    {
        session_set_save_handler(array(&$this, 'open'),
                                 array(&$this, 'close'),
                                 array(&$this, 'read'),
                                 array(&$this, 'write'),
                                 array(&$this, 'destroy_handler'),
                                 array(&$this, 'gc'));
        session_start();
    }
	
	/**
	 * 设置过期时间
	 *
	 * 设置Session的过期时间，以秒为单位
	 *
	 * @author Garbin
	 * @access public
	 * @param int $time
	 *        	过期时间(如：1800表示半个小时)
	 * @return void
	 */
	public function setMaxLifeTime($time) {
		$this->_max_life_time = intval ( $time );
	}
	
	/**
	 * 销毁Session
	 *
	 * 销毁当前Session
	 *
	 * @author Garbin
	 * @access public
	 * @return void
	 *
	 */
	public function destroy() {
		session_destroy ();
	}
	
	/**
	 * 系统回调函数Open
	 *
	 * session_set_save_handler的回调函数open，关于更多信息参看php手册
	 *
	 * @author Garbin
	 * @param string $save_path
	 *        	session保存路径
	 * @param string $sess_name
	 *        	session名
	 * @return bool
	 */
	public function open($save_path, $sess_name) {
		return true;
	}
	
	/**
	 * 系统回调函数close
	 *
	 * session_set_save_handler的回调函数close，关于更多信息参看php手册
	 *
	 * @author Garbin
	 * @return bool
	 */
	public function close() {
		return true;
	}
	
	/**
	 * 系统回调函数read
	 *
	 * session_set_save_handler的回调函数read，关于更多信息参看php手册
	 *
	 * @author Garbin
	 * @param string $id
	 *        	session_id
	 * @return string 返回保存的session数据
	 */
	public function read($id) {
		return '';
	}
	
	/**
	 * 系统回调函数write
	 *
	 * session_set_save_handler的回调函数write，关于更多信息参看php手册
	 *
	 * @author Garbin
	 * @param string $id
	 *        	session_id
	 * @param string $data
	 *        	session数据
	 * @return bool
	 */
	public function write($id, $data) {
		return true;
	}
	
	/**
	 * 系统回调函数destroy，销毁session时调用
	 *
	 * session_set_save_handler的回调函数destroy，关于更多信息参看php手册
	 *
	 * @author Garbin
	 * @param string $id
	 *        	session_id
	 * @return bool
	 */
	public function destroy_handler($id) {
		return true;
	}
	
	/**
	 * 系统回调函数gc，垃圾回收
	 *
	 * session_set_save_handler的回调函数gc，关于更多信息参看php手册
	 *
	 * @author Garbin
	 * @param int $max_life_time
	 *        	系统设置的session过期时间，在这里不使用它，使用自定义的过期时间
	 * @return bool
	 */
	public function gc($max_life_time) {
		return true;
	}
}
class DatabaseSession extends BaseSession {
	public $db = null;
	protected $_table = '';
	
	private $_now = '';
	
	function __construct(){
		$this->_now = time();
	}
	
	/**
	 * 设置数据库连接
	 *
	 * @author Silenus
	 * @access public
	 * @param object $db        	
	 * @return void
	 *
	 */
	public function setDatabaseConnection( $db) {
		$this->db = $db;
	}
	
	/**
	 * 指定存储表
	 *
	 * @author Garbin
	 * @access public
	 * @param string $table_name        	
	 * @return void
	 *
	 */
	public function setTable($table_name) {
		$this->_table = $table_name;
	}
	
	// TODO:测试
	public function gc($max_life_time) {
		$sql = "DELETE FROM {$this->_table} WHERE expiry + {$this->_max_life_time} < {$this->_now}";
		$this->db->exec ( $sql );
		
		return true;
	}
	public function read($id) {
		$sql = "SELECT data FROM {$this->_table} WHERE session_id = '{$id}' LIMIT 0,1";
		$data = array_pop( $this->db->getArray ( $sql ) );
		$return = '';
		if (! empty ( $data )) {
			$return = $data ['data'];
		}
		return $return;
	}
	public function write($id, $data) {
		/* 若不进行此处理将导致数据中有单引号后会错误 */
		$data = addslashes ( $data );
		/* 插入Session数据 */
		$expiry = $this->_now + $this->_max_life_time;
		$sql = "REPLACE INTO {$this->_table}(session_id, expiry, data) VALUES('{$id}', {$expiry}, '{$data}')";
		$this->db->exec ( $sql );
// 		echo $sql;
		return true;
	}
	public function destroy_handler($id) {
		$sql = "DELETE FROM {$this->_table} WHERE session_id='{$id}'";
		
		return $this->db->exec ( $sql );
	}
}
class DatabaseConnect {
	
	private $conn;
	public 	$arrSql;
	/**
	 * 
	 * @param array  $dbConfig
	 * host port login password database
	 */
	function __construct($dbConfig){
		$this->conn = mysql_connect($dbConfig['host'].":".$dbConfig['port'], $dbConfig['login'], $dbConfig['password']) ;
		mysql_select_db($dbConfig['database'], $this->conn) ;
		$this->exec("SET NAMES UTF8");
	}
	
	public function getArray($sql)
	{
		if( ! $result = $this->exec($sql) )return array();
		if( ! mysql_num_rows($result) )return array();
		$rows = array();
		while($rows[] = mysql_fetch_array($result,MYSQL_ASSOC)){}
		mysql_free_result($result);
		array_pop($rows);
		return $rows;
	}
	
	public function exec($sql)
	{
		$this->arrSql[] = $sql;
		if( $result = mysql_query($sql, $this->conn) ){
			return $result;
		}else{
			die("执行错误: " . mysql_error());
		}
	}
	
}
